20210926
好久没关注博客园,有些评论未回应。
如果要代码,点击下面连接:
https://files-cdn.cnblogs.com/files/warmlight/NewProject.rar
编译环境是:Qt5.9.0 MinGW 32 。
这个只是一个示例,功能不完全,也不排除代码有什么bug。如果要使用到项目中,一定要多测试。
![](https://img2020.cnblogs.com/blog/1646436/202109/1646436-20210926145437574-1126640708.png)
2020.07.13
前几天有csdn网友给我提了一个bug:在画线是paint函数会进入无线循环。
我今天修改了一下画非箭头直线的函数。具体请点击博客园的链接下载。文中再贴出修改的地方。
2020.02.27
想要代码,留邮箱吧。或者到https://download.csdn.net/download/XuePiaoFei1/12195991下载,或者https://files-cdn.cnblogs.com/files/warmlight/NewProject.rar。
近来Qt开发时可能遇到这样的需求:两个(或多个)矩形,要用直线将它们连接起来,之后还要把它们保存到xml中,并且能够还原。
类似于下图:
![](https://img2018.cnblogs.com/i-beta/1646436/202002/1646436-20200224173713144-470397290.png)
首先想到的就是Qt自带的demo:diagramscene。因为demo中有箭头方向,开始节点和结束节点,连坐标都已经有了,保存还原都可以完成。但是,demo的直线不是竖直或水平的,所以肯定要对demo进行修改。(扯淡了,demo肯定不会符合个人的需求的,无论如何都要修改的。)
刚用Qt不久,网络上也搜不到类似的问题,也许是自己找不到,只能寄希望于Qt的demo,限制了自己的思想。可是目前没有更好的办法。
我的工程有几个要点,要能够保存和还原,箭头的首尾节点肯定要确定,或者说首尾节点的坐标一定要知道。
demo中的基本元素够了,我想只对arrow类作修改,希望能达到目标。
我先把我改造后的arrow类贴出来,然后再试图分析一下。
头文件:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the examples of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:BSD$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** BSD License Usage
18 ** Alternatively, you may use this file under the terms of the BSD license
19 ** as follows:
20 **
21 ** "Redistribution and use in source and binary forms, with or without
22 ** modification, are permitted provided that the following conditions are
23 ** met:
24 ** * Redistributions of source code must retain the above copyright
25 ** notice, this list of conditions and the following disclaimer.
26 ** * Redistributions in binary form must reproduce the above copyright
27 ** notice, this list of conditions and the following disclaimer in
28 ** the documentation and/or other materials provided with the
29 ** distribution.
30 ** * Neither the name of The Qt Company Ltd nor the names of its
31 ** contributors may be used to endorse or promote products derived
32 ** from this software without specific prior written permission.
33 **
34 **
35 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
36 ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
37 ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
38 ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
39 ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41 ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
42 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
43 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
44 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
45 ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
46 **
47 ** $QT_END_LICENSE$
48 **
49 ****************************************************************************/
50
51 #ifndef ARROW_H
52 #define ARROW_H
53
54 #include
55
56 #include "diagramitem.h"
57
58 QT_BEGIN_NAMESPACE
59 class QGraphicsPolygonItem;
60 class QGraphicsLineItem;
61 class QGraphicsScene;
62 class QRectF;
63 class QGraphicsSceneMouseEvent;
64 class QPainterPath;
65 QT_END_NAMESPACE
66
67 enum LineType {
68 lineType1 = 1, //横竖
69 lineType2, //竖横
70 lineType3, //横竖横
71 lineType4 //竖横竖
72 };
73
74 //! [0]
75 class Arrow : public QGraphicsLineItem
76 {
77 public:
78 enum { Type = UserType + 4 };
79
80 Arrow(DiagramItem *startItem, DiagramItem *endItem,
81 QGraphicsItem *parent = 0);
82 ~Arrow() {m_bDeleteFlag = true;}
83
84 int type() const override { return Type; }
85 QRectF boundingRect() const override;
86 QPainterPath shape() const override;
87 void setColor(const QColor &color) { myColor = color; }
88 DiagramItem *startItem() const { return myStartItem; }
89 DiagramItem *endItem() const { return myEndItem; }
90
91 void setItemId(QString startItemId ,QString startEndId);
92 void updatePosition();
93
94 QString getStartId(){return startId;}
95 QString getEndId(){return endId;}
96
97 void setStartItem(DiagramItem *startItem){ myStartItem = startItem; }
98 void setEndItem(DiagramItem *endItem){ myEndItem = endItem; }
99
100 void setArrowFlag(bool IsArrow){bisArrow = IsArrow;}
101 bool getArrowFlag(){return bisArrow;}
102 bool getDeleteFlage() {return m_bDeleteFlag;}
103 void DrawArrow(QPainter *painter, QPointF startPt, QPointF endPt, bool bArrow);
104 void ThreeLine(QPainter *painter, QPointF startPt, QPointF endPt);//三段线
105 void TwoLine(QPainter *painter, QPointF startPt, QPointF endPt); //两段线
106 //void SetLineStyle(bool bVhvLine);//线段的折线风格
107 //void SetLineType(int nLineType);//线段的数量
108 void SetLineType(LineType lineType);
109 int GetLineType() {return m_lineType;}
110 void DrawLine(QPainter *painter, QPointF startPt, QPointF endPt);
111 protected:
112 void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0) override;
113
114 private:
115 DiagramItem *myStartItem;
116 DiagramItem *myEndItem;
117 QColor myColor;
118 QPolygonF arrowHead;
119 QString startId;
120 QString endId;
121 bool bisArrow;
122 bool m_bDeleteFlag;//
123 LineType m_lineType;
124 //bool m_bVhvLine;//true三段线,竖横竖; false三段线 横竖横
125 //bool m_bVhLine;// true两段线 竖横; false 两段线 横竖
126 //int m_nLineType;//2-2段线,3-3段线
127 };
128
129 inline void Arrow::setItemId(QString startItemId ,QString startEndId){
130 startId = startItemId;
131 endId = startEndId;
132 }
133 //! [0]
134
135 #endif // ARROW_H
View Code
cpp文件:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the examples of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:BSD$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** BSD License Usage
18 ** Alternatively, you may use this file under the terms of the BSD license
19 ** as follows:
20 **
21 ** "Redistribution and use in source and binary forms, with or without
22 ** modification, are permitted provided that the following conditions are
23 ** met:
24 ** * Redistributions of source code must retain the above copyright
25 ** notice, this list of conditions and the following disclaimer.
26 ** * Redistributions in binary form must reproduce the above copyright
27 ** notice, this list of conditions and the following disclaimer in
28 ** the documentation and/or other materials provided with the
29 ** distribution.
30 ** * Neither the name of The Qt Company Ltd nor the names of its
31 ** contributors may be used to endorse or promote products derived
32 ** from this software without specific prior written permission.
33 **
34 **
35 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
36 ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
37 ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
38 ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
39 ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41 ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
42 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
43 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
44 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
45 ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
46 **
47 ** $QT_END_LICENSE$
48 **
49 ****************************************************************************/
50 #include "arrow.h"
51 #include
52 #include
53 #include
54 #include
55
56 //! [0]
57 Arrow::Arrow(DiagramItem *startItem, DiagramItem *endItem, QGraphicsItem *parent)
58 : QGraphicsLineItem(parent)
59 {
60 m_bDeleteFlag = false;//20191121 sdl
61 myStartItem = startItem;
62 myEndItem = endItem;
63 setFlag(QGraphicsItem::ItemIsSelectable, true);
64 myColor = Qt::black;
65 setPen(QPen(myColor, 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
66 bisArrow = true;
67 if(myStartItem){startId = myStartItem->getItemId();}
68 if(myEndItem){endId = myEndItem->getItemId();}
69 m_lineType = LineType::lineType1;
70 }
71 //! [0]
72
73 //! [1]
74 QRectF Arrow::boundingRect() const
75 {
76 qreal extra = (pen().width() + 20) / 2.0;
77
78 return QRectF(line().p1(), QSizeF(line().p2().x() - line().p1().x(),
79 line().p2().y() - line().p1().y()))
80 .normalized()
81 .adjusted(-extra, -extra, extra, extra);
82 }
83 //! [1]
84
85 //! [2]
86 QPainterPath Arrow::shape() const
87 {
88 QPainterPath path = QGraphicsLineItem::shape();
89 path.addPolygon(arrowHead);
90 return path;
91 }
92 //! [2]
93
94 //! [3]
95 void Arrow::updatePosition()
96 {
97 QPointF endPt = mapFromItem(myEndItem, 0, 0);
98 QLineF line(mapFromItem(myStartItem, 0, 0), endPt);
99 setLine(line);
100 }
101
102
103 //! [3]
104
105 //! [4]
106 void Arrow::paint(QPainter *painter, const QStyleOptionGraphicsItem *,
107 QWidget *)
108 {
109 if(myStartItem == NULL || myEndItem == NULL) return;
110 if(myStartItem->GetDeleteFlag() || myEndItem->GetDeleteFlag()) return;
111 if (myStartItem->collidesWithItem(myEndItem))
112 return;
113
114 QPointF start = myStartItem->pos();
115 QPointF end = myEndItem->pos();
116 if(qFuzzyCompare(start.x(), end.x())){ //如果在同一竖直或水平线时,直接连接两节点中心点
117 DrawArrow(painter, start, end, true);
118 return;
119 }
120 if (qFuzzyCompare(start.y(), end.y())) {
121 DrawArrow(painter, start, end, true);
122 return;
123 }
124
125 DrawLine(painter, start, end);
126 // if(m_nLineType == 2){
127 // TwoLine(painter, start, end);
128 // }
129 // else if (m_nLineType == 3) {
130 // ThreeLine(painter, start, end);
131 // }
132 }
133 //! [7]
134
135 void Arrow::DrawArrow(QPainter *painter, QPointF startPt, QPointF endPt, bool bArrow)
136 {
137 if(endPt == QPointF(0, 0)){
138 return;
139 }
140 QPen myPen = pen();
141 myPen.setColor(myColor);
142 qreal arrowSize = 10;
143 painter->setPen(myPen);
144 painter->setBrush(myColor);
145 //! [4] //! [5]
146
147 QLineF centerLine(startPt, endPt);
148
149 QPolygonF endPolygon;
150 endPolygon = myEndItem->polygon();
151 QPointF p1 = endPolygon.first() + endPt;
152 QPointF p2;
153 QPointF intersectPoint;
154 QLineF polyLine;
155 for (int i = 1; i < endPolygon.count(); ++i) {
156 p2 = endPolygon.at(i) + endPt;
157 polyLine = QLineF(p1, p2);
158 QLineF::IntersectType intersectType =
159 polyLine.intersect(centerLine, &intersectPoint);
160 if (intersectType == QLineF::BoundedIntersection)
161 break;
162 p1 = p2;
163 }
164
165 setLine(QLineF(endPt, startPt/*myStartItem->pos()*/));
166
167 //! [5] //! [6]
168 if (bArrow){
169 setLine(QLineF(intersectPoint, startPt/*myStartItem->pos()*/));//如果是箭头所在的线
170 }
171 double angle = std::atan2(-line().dy(), line().dx());
172
173 QPointF arrowP1 = line().p1() + QPointF(sin(angle + M_PI / 3) * arrowSize,
174 cos(angle + M_PI / 3) * arrowSize);
175 QPointF arrowP2 = line().p1() + QPointF(sin(angle + M_PI - M_PI / 3) * arrowSize,
176 cos(angle + M_PI - M_PI / 3) * arrowSize);
177
178 arrowHead.clear();
179 arrowHead drawLine(myLine);
191 myLine.translate(0,-8.0);
192 painter->drawLine(myLine);
193 }
194 }
195
196 void Arrow::ThreeLine(QPainter *painter, QPointF start, QPointF end)
197 {
198 if(m_lineType == LineType::lineType4){
199 qreal midY = (start.y() + end.y()) / 2;
200 QPointF startPt = myStartItem->pos();
201 QPointF endPt = myEndItem->pos();
202
203 endPt.setX(startPt.x());
204 endPt.setY(midY);
205 DrawArrow(painter, startPt, endPt, false);
206
207 startPt = endPt;
208 endPt.setX(myEndItem->pos().x());
209 endPt.setY(startPt.y());
210 DrawArrow(painter, startPt, endPt, false);
211
212 startPt = endPt;
213 endPt = myEndItem->pos();
214
215 DrawArrow(painter, startPt, endPt, true);
216 }
217 else if(m_lineType == LineType::lineType3){
218 qreal midX = (start.x() + end.x()) / 2;
219 QPointF startPt = myStartItem->pos();
220 QPointF endPt = myEndItem->pos();
221
222 endPt.setX(midX);
223 endPt.setY(startPt.y());
224 DrawArrow(painter, startPt, endPt, false);
225
226 startPt = endPt;
227 endPt.setX(startPt.x());
228 endPt.setY(myEndItem->pos().y());
229 DrawArrow(painter, startPt, endPt, false);
230
231 startPt = endPt;
232 endPt = myEndItem->pos();
233 DrawArrow(painter, startPt, endPt, true);
234 }
235 }
236
237 void Arrow::TwoLine(QPainter *painter, QPointF startPt, QPointF endPt)
238 {
239 if(m_lineType == LineType::lineType2){
240 endPt.setX(startPt.x());
241 endPt.setY(endPt.y());
242 DrawArrow(painter, startPt, endPt, false);
243
244 startPt = endPt;
245 endPt = myEndItem->pos();
246 DrawArrow(painter, startPt, endPt, true);
247 }
248 else if(m_lineType == LineType::lineType1){
249 endPt.setY(startPt.y());
250 DrawArrow(painter, startPt, endPt, false);
251
252 startPt = endPt;
253 endPt = myEndItem->pos();
254 DrawArrow(painter, startPt, endPt, true);
255 }
256 }
257
258 //void Arrow::SetLineStyle(bool bVhvLine)
259 //{
260 // m_bVhvLine = bVhvLine;
261 //}
262
263 void Arrow::SetLineType(LineType lineType)
264 {
265 if(lineType > lineType4 || lineType pos();
277 QPointF endPt = myEndItem->pos();
278
279 endPt.setX(startPt.x());
280 endPt.setY(midY);
281 DrawArrow(painter, startPt, endPt, false);
282
283 startPt = endPt;
284 endPt.setX(myEndItem->pos().x());
285 endPt.setY(startPt.y());
286 DrawArrow(painter, startPt, endPt, false);
287
288 startPt = endPt;
289 endPt = myEndItem->pos();
290
291 DrawArrow(painter, startPt, endPt, true);
292 }
293 else if(m_lineType == LineType::lineType3){
294 qreal midX = (start.x() + end.x()) / 2;
295 QPointF startPt = myStartItem->pos();
296 QPointF endPt = myEndItem->pos();
297
298 endPt.setX(midX);
299 endPt.setY(startPt.y());
300 DrawArrow(painter, startPt, endPt, false);
301
302 startPt = endPt;
303 endPt.setX(startPt.x());
304 endPt.setY(myEndItem->pos().y());
305 DrawArrow(painter, startPt, endPt, false);
306
307 startPt = endPt;
308 endPt = myEndItem->pos();
309 DrawArrow(painter, startPt, endPt, true);
310 }
311 else if(m_lineType == LineType::lineType2){
312 end.setX(start.x());
313 end.setY(end.y());
314 DrawArrow(painter, start, end, false);
315
316 start = end;
317 end = myEndItem->pos();
318 DrawArrow(painter, start, end, true);
319 }
320 else if(m_lineType == LineType::lineType1){
321 end.setY(start.y());
322 DrawArrow(painter, start, end, false);
323
324 start = end;
325 end = myEndItem->pos();
326 DrawArrow(painter, start, end, true);
327 }
328 else {//默认是linetype1
329 m_lineType = LineType::lineType1;
330 end.setY(start.y());
331 DrawArrow(painter, start, end, false);
332
333 start = end;
334 end = myEndItem->pos();
335 DrawArrow(painter, start, end, true);
336 }
337 }
View Code
2020.07.13-------------
为了防止paint进入死循环,修改了DrawArrow函数,另外改了选中线时的操作。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 void Arrow::DrawArrow(QPainter *painter, QPointF startPt, QPointF endPt, bool bArrow)
2 {
3 if(endPt == QPointF(0, 0)){
4 return;
5 }
6 QPen myPen = pen();
7 myPen.setColor(myColor);
8 qreal arrowSize = 10;
9 painter->setPen(myPen);
10 painter->setBrush(myColor);
11
12 QLineF centerLine(startPt, endPt);
13 QPolygonF endPolygon;
14 endPolygon = myEndItem->polygon();
15 QPointF p1 = endPolygon.first() + endPt;
16 QPointF p2;
17 QPointF intersectPoint;
18 QLineF polyLine;
19 for (int i = 1; i < endPolygon.count(); ++i) {
20 p2 = endPolygon.at(i) + endPt;
21 polyLine = QLineF(p1, p2);
22 QLineF::IntersectType intersectType =
23 polyLine.intersect(centerLine, &intersectPoint);
24 if (intersectType == QLineF::BoundedIntersection)
25 break;
26 p1 = p2;
27 }
28
29 qDebug() drawLine(myLine);
60 myLine.translate(0,-8.0);
61 painter->drawLine(myLine);
62 }
63 }
View Code
2020.07.13------------
这个类中,有一些代码冗余。
我在这里分了四类画线方式,即头文件中的LineType。在横竖横和竖横竖的类型中,中间线段取的是中点画线。箭头首尾的节点都有了,保存也可以的。也可以根据个人情况修改箭头类。
操作:编辑图形时,基本图形时拖拽到场景中的。连线不是拖拽,是单击选中一种类型,在场景中连接两个基本图形即可。选中一个画线方式,再次拖拽基本图形或右键鼠标后,需要再次选择画线方式,才能再次画线。右键鼠标会出现右键菜单,打开、保存、删除。保存,保存场景中图形为xml;打开,打开保存的xml文件;删除,可删除基本图形或者箭头。注意:删除基本图形时,如果基本图形有连线,连线也会被删除。
还有一些功能待完善,如修改基本图形的名称等。
结果图:
![](https://img2018.cnblogs.com/i-beta/1646436/202002/1646436-20200227163712326-1389094149.png)
![](https://img2018.cnblogs.com/i-beta/1646436/202002/1646436-20200227164115962-1955941939.png)
稍后把代码传到csdn。如果博客园能上传代码就好了。
|